home *** CD-ROM | disk | FTP | other *** search
Text File | 1990-06-24 | 30.0 KB | 699 lines | [TEXT/CCL ] |
- ; (c) Copyright 1990 by University of Massachusetts. All rights reserved.
- ; This software was conceived, designed, and written by Dan Suthers
- ; while supported by the National Science Foundation under grant number
- ; MDR 8751362, and by a fellowship from Apple Computer, Inc., Cupertino,
- ; CA. Partial support was also received from the Office of Naval Research
- ; under a University Research Initiative Grant, contract N00014-86-K-0764.
- ; Mr. Suthers created this software under his own initiative while in an
- ; academic relationship with the University of Massachusetts. The above
- ; copyright notice was a condition placed by University lawyers on approval
- ; of distribution of this software by Apple Computer, and is not meant to
- ; imply that this software was created in an employment or "work for hire"
- ; relationship between the University and Mr. Suthers.
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- ; File: DIALOGUE.lisp
- ; Author: Dan Suthers
- ; Created: 27-Nov-87 20:38:00
- ; Modified: 24-Jun-90 23:43:30 (Dan Suthers)
- ; Language: LISP
- ; Package: WIND
- ;
- ; Description: Defines "Dialogue" functions for interactions such as
- ; informing the user of something, asking a y-n or multiple
- ; choice question, etc. These will be in popup windows on
- ; some machines, and ASCII terminal I/O on all others.
- ;
- ; (c) Copyright 1987, by Daniel D. Suthers
- ; Department of Computer and Information Science
- ; University of Massachusetts
- ; Amherst, Massachusetts 01003
- ;
- ; This software was conceived, designed, and written by Dan Suthers
- ; while supported by the National Science Foundation under grant number
- ; MDR 8751362, and by a fellowship from Apple Computer, Inc., Cupertino,
- ; CA. Partial support was also received from the Office of Naval Research
- ; under a University Research Initiative Grant, contract N00014-86-K-0764.
- ; I wish to acknowledge the generous support of Beverly Woolf, who obtained
- ; the above grants and encouraged me to pursue my own research interests in
- ; her lab. This work would not have been possible without the resources and
- ; stimulating environment of the Computer and Information Science department.
- ;
- ; Permission to use, modify, and distribute this software is granted subject
- ; to the following restrictions and understandings:
- ; 1. The file header, including this notice, shall be retained, and may be
- ; extended to include documentation of modifications to the software.
- ; 2. This material is for nonprofit educational and research purposes only.
- ; Users are requested, but not required, to inform Mr. Suthers of any
- ; noteworthy uses of this software.
- ; 3. Mr. Suthers and the University of Massachusetts make no warantee or
- ; representation that the operation of this software will be error free,
- ; and are under no obligation to provide any services.
- ; 4. Any user of such software agrees to indemnify and hold harmless Mr.
- ; Suthers and the University of Massachusetts from all claims arising
- ; out of the use or misuse of this software, or arising out of any
- ; accident, injury, or damage whatsoever, and from all costs, counsel
- ; fees, and liabilities incurred in or about any such claim, action, or
- ; proceeding brought thereon.
- ; 5. All materials and reports developed as a consequence of the use of
- ; this software shall duly acknowledge such use, in accordance with
- ; the usual standards of acknowledging credit in academic research.
- ;
- ; Status: Approved for distribution.
- ; Supported machines and last test dates:
- ; Hewlett Packard 9000 02/27/88 Dan Suthers
- ; Windows 9000 (if :W9000 on *features*), ASCII
- ; Macintosh II Coral/Allegro 12/14/89 Dan Suthers
- ; Coral Common Lisp Dialogues used.
- ; Texas Instruments Explorer 01/20/88 Dan Suthers
- ; TV package (version 2) popup windows used.
- ; (Needs version 3 rewrite)
- ; VAX/VMS 02/27/88 Dan Suthers
- ; Plain vanilla ASCII interaction tested here.
- ;
- ; Changes:
- ; 24-Jan-88 Added get-string-default-dialogue
- ; 28-Jan-88 CCL Screen dependent constants -> parameters
- ; 31-Jan-88 get-string-default-dialogue adjusts size for large default strings.
- ; Values of message-size reversed.
- ; 01-Feb-88 Allegro: get-string dialogues made a touch wider.
- ; 04-Feb-88 Allegro: Minor changes to get rid of whitespace, and cancel is no
- ; longer default button.
- ; 11-Feb-88 HP: Added trim-right-margin to w9000 version.
- ; 27-Feb-88 HP: Changed windows9000 file to push :W9000 on *features*. User now
- ; responsible for loading that file, and #+:W9000 instead of #+HP.
- ; 28-Feb-88 Menu-dialogue now has OK button, the mouse click selects item but
- ; does not return from menu as before.
- ; 07-Jul-88 Updated for Allegro 1.2.
- ; 07-Jan-89 Bigger get-string-dialogue entry item.
- ; 17-Apr-89 Optimization declarations added. Also size of CCL dialogues
- ; now computed for messages and get-string.
- ; 06-Nov-89 Menu dialogues default selection to first item for convenience.
- ; 14-Dec-89 Fixed menus to allow double click return.
- ; 21-Dec-89 To satisfy need to specify popup location in CCL without loosing
- ; upward compatibility, added somewhat ugly *dialogue-position* mechanism.
- ; 11-Jan-90 Menus now show 7 items; 5 was cramped for fast scrolling.
- ; 30-Jan-90 Updated for version 1.3.1 (:default-button has to be specified
- ; in button item's init list).
- ; 17-Feb-90 Added *multiple-menu-cells-to-select*, a hack to let some
- ; applications say what cells should be selected in CCL. Added
- ; window-key-event-handler to menus to scroll to item starting with
- ; character given.
- ; 26-Apr-90 DS Sick of CCL's bad computation of y-or-n-dialog size, I
- ; now compute size myself so text is not cut off. Also reorganized
- ; all size computations to make better use of MESSAGE-SIZE-IN-POINTS,
- ; and added MIN-RECT and SCREEN-RECT to help set limits. MAX-RECT
- ; fixed to evaluate its arguments only once.
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
- (in-package 'WIND :use '("LISP"))
-
- (export '(
- *dialogue-position*
- *multiple-menu-cells-to-select*
-
- message-dialogue
- y-or-n-dialogue
- get-string-default-dialogue
- get-string-dialogue
- menu-dialogue
- multiple-menu-dialogue
-
- ;; Helpers which may be of use ...
- message-size
- message-size-in-points
- trim-right-margin
-
- ))
-
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
- (defconstant *NEWLINE* (char (format nil "~%") 0))
-
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- ;;;
- ;;; HELPERS
- ;;;
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
- ;;; Returns two values: number of columns and number of rows needed to
- ;;; print the message in an ascii-based window.
-
- (defun MESSAGE-SIZE (message)
- (declare (optimize (safety 1) (space 2) (speed 3)))
- (do ((i 0 (1+ i))
- (messlen (length message))
- (colcount 0)
- (maxcol 1)
- (maxrow 1))
- ((= i messlen) (values (max colcount maxcol) maxrow))
- (declare (fixnum i messlen colcount maxcol maxrow))
- (cond ((eql (char message i) *newline*)
- (incf maxrow)
- (if (> colcount maxcol) (setq maxcol colcount))
- (setq colcount 0))
- ((incf colcount)))))
- (proclaim '(inline message-size))
-
- ;;; Reformat a string to be within a given right margin width, by inserting
- ;;; newlines where there were spaces. If this is not possible, it gives up.
-
- (defun TRIM-RIGHT-MARGIN (message margin-column)
- "trim-right-margin <message> <margin-column> [Function]
- Returns a string with the same contents except newlines are inserted
- to trim to the indicated margin."
- (declare (optimize (safety 1) (space 2) (speed 3)))
- (do ((ptr 0 (1+ ptr)) ; traverses message
- (position 0 (1+ position)) ; counts current column
- (messlen (length message)))
- ((= ptr messlen) message)
- (declare (fixnum ptr position messlen))
- (cond ((eql (char message ptr) *newline*)
- (setf position 0))
- ((> position margin-column)
- ;; Must break before here. Search back to space and
- ;; replace it with newline.
- (do ((backptr ptr (1- backptr)))
- ((<= backptr 0))
- (declare (fixnum backptr))
- (if (eql (char message backptr) #\ )
- (setf (char message backptr) *newline*
- ptr backptr ; restart here
- backptr 0
- position 0)))))))
- (proclaim '(inline trim-right-margin))
-
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- ;;;
- ;;; TI EXPLORERS
- ;;;
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
- #+:TI (use-package 'tv)
- ;;; Makes a choice list into an item list, since the displayed label
- ;;; and the value returned are distinguished on the TI.
-
- #+:TI
- (defun MAKE-ITEM-LIST (choices)
- (declare (optimize (safety 1) (space 2) (speed 3)))
- (mapcar #'(lambda (choice) (list choice :value choice))
- choices))
- #+:TI (proclaim '(inline make-item-list))
-
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- ;;;
- ;;; CORAL COMMON LISP
- ;;;
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
- #+:CCL (use-package :ccl)
-
- #+:CCL
- (defvar *DIALOGUE-POSITION* :centered
- "Determines where popup dialogues in this package appear. (Upward
- compatibility precludes an additional argument to the functions.)")
-
- #+:CCL
- (defvar *MULTIPLE-MENU-CELLS-TO-SELECT* nil
- "Determines what cells are selected in multiple-menu-dialogue.
- Should be a list of index positions.")
-
- #+:CCL
- (defmacro MESSAGE-SIZE-IN-POINTS (message)
- ;; Computes rectangle space in points required to represent the message
- ;; in the standard dialog font.
- `(multiple-value-bind
- (columns rows)
- (message-size ,message)
- (declare (fixnum columns rows))
- ;; Compensate for excess padding in big messages.
- (make-point (if (< columns 50) (* 9 columns) (* 8 columns))
- (if (< rows 5) (* 22 rows) (* 20 rows)))))
-
- #+:CCL
- (eval-when (compile eval)
-
- (defmacro MAX-RECT (point1 point2)
- ;; Returns a rectangle point with h,v maximum of those in points.
- `(let* ((point1-val ,point1)
- (point2-val ,point2)
- (point1-h (point-h point1-val))
- (point1-v (point-v point1-val))
- (point2-h (point-h point2-val))
- (point2-v (point-v point2-val)))
- (declare (fixnum point1-h point1-v point2-h point2-v))
- (make-point (max point1-h point2-h) (max point1-v point2-v))))
-
- (defmacro MIN-RECT (point1 point2)
- ;; Returns a rectangle point with h,v minimum of those in points.
- `(let* ((point1-val ,point1)
- (point2-val ,point2)
- (point1-h (point-h point1-val))
- (point1-v (point-v point1-val))
- (point2-h (point-h point2-val))
- (point2-v (point-v point2-val)))
- (declare (fixnum point1-h point1-v point2-h point2-v))
- (make-point (min point1-h point2-h) (min point1-v point2-v))))
-
- (defmacro SCREEN-RECT ()
- '(make-point *screen-width*
- (- *screen-height* *menubar-bottom*)))
-
- ) ; eval-when
-
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- ;;;
- ;;; HP 9000
- ;;; Windows 9000 and X Windows
- ;;;
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- ;;;
- ;;; There are three potential ways of interacting on the HP 9000: X Windows,
- ;;; Windows9000, or ASCII. The user is responsible for first loading any
- ;;; support files that are needed for the window system, and recording the
- ;;; loaded window system on *featurs*. For example, the Windows9000.l file
- ;;; has been patched to push :W9000 onto *features*.
- ;;;
- ;;; DIALOGUE has been written so that loading the same UNcompiled source will
- ;;; work on any machine and window system it supports. Unfortuantely there
- ;;; is a tradeoff for compiled files. Either we have unnecessary code in
- ;;; the compiled version on a given machine (eg. run time conditionalization
- ;;; for HP's between W9000, X, and ASCII), or we need multiple versions of
- ;;; compiled DIALOGUE.b, one for each window system. The latter option has
- ;;; been taken, for efficiency and to simplify the following code (not mix
- ;;; #+ and run time conditionalization constructs). It is suggested that
- ;;; the user place the appropriate version of DIALOGUE.b in the same
- ;;; directory as the supporting windows file, with the ASCII version of
- ;;; DIALOGUE.b in the generic utilities directory. Then one can switch
- ;;; between window systems by changing which directory appears first on HP
- ;;; Common Lisp's sys:*require-directories*.
- ;;;
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- ;;; Windows 9000
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
- #+:W9000 (use-package :W9000)
-
- #+:W9000 (defvar *popup-fd* nil)
- #+:W9000 (defvar *popup-stream* nil)
- #+:W9000 (defvar *popup-path* (make-string 50))
-
- #+:W9000 (create-window *popup-fd* *popup-stream* "POPUP"
- *popup-path* 5 5 60 10 60 10 60 10
- "/usr/lib/raster/8x16/lp.8U"
- "/usr/lib/raster/8x16/lp.b.8U"
- 2 0)
-
- ;;; Our W9000 menus can't deal with line feeds in labels. This will turn
- ;;; a single string with line feeds into a list of strings.
-
- #+:W9000
- (defun SPLIT-LINES (string)
- (declare (optimize (safety 1) (space 2) (speed 3)))
- (do ((lines nil)
- (pos nil))
- ((string= string "") (reverse lines))
- (setq pos (position *newline* string))
- (if pos
- (progn (push (subseq string 0 pos) lines)
- (setq string (subseq string (1+ pos) (length string))))
- (progn (push string lines)
- (setq string "")))))
- #+:W9000 (proclaim '(inline split-lines))
-
- #+:W9000
- (defun MAKE-ITEM-LIST (choices)
- (declare (optimize (safety 1) (space 2) (speed 3)))
- (mapcar #'(lambda (opt)
- (list (cond ((or (numberp opt) (listp opt))
- (format nil "~A" opt))
- (t (string opt)))
- opt))
- choices))
- #+:W9000 (proclaim '(inline make-item-list))
-
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- ;;;
- ;;; THE FUNCTIONS
- ;;;
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
- (defun MESSAGE-DIALOGUE (format &rest args &aux message)
- "message-dialogue <format> &rest <args> - Function
- Displays a message in a pop-up window, returning NIL after a
- user mouse click or keyboard action (depending on the machine)."
- (declare (optimize (safety 1) (space 2) (speed 3)))
- (setq message (apply #'format nil format args))
- #+:CCL (progn
- (trim-right-margin message 70)
- (message-dialog message
- :position *dialogue-position*
- :size (min-rect
- (add-points (message-size-in-points message)
- #.(make-point 80 30)) ; for buttons
- (screen-rect))))
- #+:TI
- (tv:mouse-confirm message " Click here or move mouse when done reading ")
- #+:W9000
- (do ((response nil)
- (menu-items (nconc (split-lines (trim-right-margin message 60))
- (list :line
- '(" Click here when done reading " T)))))
- (response (second response))
- (setq response (W9000:make-and-activate-menu
- *popup-fd* " Message Dialogue " menu-items)))
- #-(or :ccl :ti :W9000)
- (progn
- (format T "~%~A" (trim-right-margin message 60))
- (format T "~%(Press Return to continue):")
- (clear-input T)
- (read-char)) ; VAX read-char only returned on RETURN!
- nil)
-
- (defun Y-OR-N-DIALOGUE (format &rest args &aux message)
- "y-or-n-dialogue <format> &rest <args> - Function
- Does a popup-window based y-or-n-p, using format arguments."
- (declare (optimize (safety 1) (space 2) (speed 3)))
- (setq message (apply #'format nil format args))
- #+:CCL
- (progn
- (trim-right-margin message 60)
- (y-or-n-dialog
- message
- :position *dialogue-position*
- :size (min-rect (max-rect (add-points (message-size-in-points message)
- #.(make-point 10 100)) ; for buttons
- #.(make-point 220 100)) ; minimum size
- (screen-rect))))
-
-
- #+:TI
- (tv:mouse-confirm message)
- #+:W9000
- (do ((response nil)
- (menu-items (nconc (split-lines (trim-right-margin message 60))
- (list :line
- '(" Click here for YES " T)
- '(" Click here for NO " nil)))))
- (response (second response))
- (setq response (make-and-activate-menu
- *popup-fd* " Yes or No Dialogue " menu-items)))
- #-(or :ccl :ti :W9000)
- (y-or-n-p (trim-right-margin message 60)))
-
- (defun GET-STRING-DEFAULT-DIALOGUE (default format &rest args &aux message)
- "get-string-default-dialogue <default> <format> &rest <args> - Function
- Displays a formatted message in a popup window (machine permitting),
- and asks the user to enter response read in as a string. The first
- argument is the default string, returned if the user responds with
- a return. Otherwise identical to get-string-dialogue."
- (declare (optimize (safety 1) (space 2) (speed 3)))
- (setq message (apply #'format nil format args))
- #+:CCL (progn
- (trim-right-margin message 60)
- (get-string-from-user
- message default
- :position *dialogue-position*
- :size (min-rect (max-rect (add-points (message-size-in-points message)
- #.(make-point 80 70)) ; for buttons
- #.(make-point 400 80)) ; minimum size
- (screen-rect))))
- #-(or :ccl)
- (progn
- (format t "~%~A~%Default: ~S~%(Enter response, or Return for default):"
- (trim-right-margin message 60) default)
- (clear-input T)
- (read-line)))
-
- (defun GET-STRING-DIALOGUE (format &rest args)
- "get-string-dialogue <format> &rest <args> - Function
- Displays a formatted message in a popup window (machine permitting),
- and asks the user to enter response read in as a string."
- (declare (optimize (safety 1) (space 2) (speed 3)))
- (apply #'get-string-default-dialogue "" format args))
-
- ;;; CCL's select-item-from-list does not allow multi-line prompts.
-
- (defun MENU-DIALOGUE (choices format &rest args &aux message)
- "menu-dialogue <choices> <format> &rest <args> - Function
- Prompting with a message constructed from <format> applied to <args>,
- provides a menu of <choices>, of which the user may choose exactly
- one. Returns the chosen item."
- (declare (optimize (safety 1) (space 2) (speed 3)))
- (setq message (apply #'format nil format args))
- #+:CCL
- (progn
- (trim-right-margin message 60)
- (let* ((message-item
- (oneof *static-text-dialog-item*
- :dialog-item-text message
- :dialog-item-size (message-size-in-points message)))
- (menu-item
- (oneof *sequence-dialog-item*
- :dialog-item-position
- (make-point 5 (+ 10 (ask message-item (point-v (dialog-item-size)))))
- :dialog-item-size (make-point 350 142)
- :cell-size (make-point 350 16)
- :table-vscrollp t
- :visible-dimensions (make-point 1 7)
- :table-sequence choices
- :dialog-item-action
- '(if (double-click-p)
- (if (selected-cells)
- (return-from-modal-dialog
- (cell-contents (first (selected-cells))))
- (ed-beep)))))
- (ok-button
- (oneof *button-dialog-item*
- :dialog-item-text " OK "
- :dialog-item-position
- (make-point 395
- (+ 20 (point-v (ask message-item (dialog-item-size)))))
- :dialog-item-action
- #'(lambda ()
- (ask menu-item
- (if (selected-cells)
- (return-from-modal-dialog
- (cell-contents (first (selected-cells))))
- (ed-beep))))
- :default-button t))
- (cancel-button
- (oneof *button-dialog-item*
- :dialog-item-text "CANCEL"
- :dialog-item-position
- (make-point 385
- (+ 60 (point-v (ask message-item (dialog-item-size)))))
- :dialog-item-action
- #'(lambda () (return-from-modal-dialog :cancel))))
- ;; window size
- (w-h
- (min (- *screen-width* 20)
- (max
- (+ 10 (point-h (ask message-item (dialog-item-size))))
- (+ 100 (point-h (ask menu-item (dialog-item-size)))))))
- (w-v
- (min (- *screen-height* *menubar-bottom* 20)
- (+ (point-v (ask message-item (dialog-item-size)))
- (point-v (ask menu-item (dialog-item-size))))))
- (the-dialogue
- (oneof *dialog*
- :window-title "Menu Dialogue"
- :window-position *dialogue-position*
- :window-size (make-point w-h w-v)
- :window-show t
- :window-type :double-edge-box
- :dialog-items
- (list ok-button cancel-button message-item menu-item))))
- (declare (fixnum w-h w-v))
- ;; Alpha keystrokes will scroll sequence item to the first entry starting
- ;; with <char>. (Assumes list is sorted.)
- (defobfun (window-key-event-handler the-dialogue) (char &aux cell)
- (if (graphic-char-p char)
- (ask menu-item
- ;; find first cell matching (in the first dimension)
- (dotimes (c (point-v (table-dimensions)) (ed-beep))
- (setf cell (make-point 0 c))
- (when (char-equal (aref (format nil "~A" (cell-contents cell)) 0)
- char)
- ;; deselect old cell(s)
- (dolist (cell (selected-cells))
- (cell-deselect cell))
- ;; select it and scroll to it (no beep)
- (cell-select cell)
- (scroll-to-cell cell)
- (return nil))))
- (usual-window-key-event-handler char)))
- (if choices (ask menu-item (cell-select (index-to-cell 0))))
- (modal-dialog the-dialogue)))
- ;; Will have to rewrite for version 3.
- #+:TI
- (do ((chosen nil))
- (chosen chosen)
- (setq chosen (tv:menu-choose (make-item-list choices) message)))
- #+:W9000
- (do ((result nil)
- (menu-items (nconc (split-lines (trim-right-margin message 60))
- (list :line
- " (Choose one of the listed items with the mouse) ")
- (make-item-list choices))))
- ((or (null choices) result) result)
- (setq result
- (second (make-and-activate-menu
- *popup-fd* " Menu Dialogue " menu-items)))
- (if (not (member result choices :test #'equal))
- (setq result nil)))
- #-(or :ccl :ti :W9000)
- (do ((result nil))
- ((or (null choices)
- (member result choices :test #'equal))
- result)
- (format T "~%~A" (trim-right-margin message 60))
- (format T "~{~% ~35S~^ ~S~}" choices)
- (format T "~%(Enter one of the above):")
- (setf result (read))
- (if (not (member result choices :test #'equal))
- (format T "~%*** Please enter a Lisp form identical to one of the~
- ~% above (symbols in the same package, etc.) ..."))))
-
- (defun MULTIPLE-MENU-DIALOGUE (choices format &rest args &aux message)
- "multiple-menu-dialogue <choices> <format> &rest <args> - Function
- Prompting with a message constructed from <format> applied to <args>,
- provides a menu of <choices>, any number of which the user may choose.
- Returns a list of the choices."
- (declare (optimize (safety 1) (space 2) (speed 3)))
- (setq message (apply #'format nil format args))
- #+:CCL
- (progn
- (trim-right-margin message 60)
- (let* ((message-item
- (oneof *static-text-dialog-item*
- :dialog-item-text message
- :dialog-item-size (message-size-in-points message)))
- (menu-item
- (oneof *sequence-dialog-item*
- :dialog-item-position
- (make-point 5 (+ 5 (ask message-item (point-v (dialog-item-size)))))
- :dialog-item-size (make-point 350 142)
- :cell-size (make-point 350 16)
- :table-vscrollp t
- :visible-dimensions (make-point 1 7)
- :table-sequence choices
- :selection-type :disjoint
- :dialog-item-action
- '(if (double-click-p)
- (return-from-modal-dialog
- (mapcar #'cell-contents (selected-cells))))))
- (ok-button
- (oneof *button-dialog-item*
- :dialog-item-text " OK "
- :dialog-item-position
- (make-point 395
- (+ 20 (point-v (ask message-item (dialog-item-size)))))
- :dialog-item-action
- #'(lambda ()
- (ask menu-item
- (return-from-modal-dialog
- (mapcar #'cell-contents (selected-cells)))))
- :default-button t))
- (cancel-button
- (oneof *button-dialog-item*
- :dialog-item-text "CANCEL"
- :dialog-item-position
- (make-point 385
- (+ 60 (point-v (ask message-item (dialog-item-size)))))
- :dialog-item-action
- #'(lambda () (return-from-modal-dialog :cancel))))
- ;; window size
- (w-h
- (min (- *screen-width* 20)
- (max
- (+ 10 (point-h (ask message-item (dialog-item-size))))
- (+ 100 (point-h (ask menu-item (dialog-item-size)))))))
- (w-v
- (min (- *screen-height* *menubar-bottom* 20)
- (+ (point-v (ask message-item (dialog-item-size)))
- (point-v (ask menu-item (dialog-item-size))))))
- (the-dialogue
- (oneof *dialog*
- :window-title "Multiple Menu Dialogue"
- :window-position *dialogue-position*
- :window-size (make-point w-h w-v)
- :window-show t
- :window-type :double-edge-box
- :dialog-items
- (list ok-button cancel-button message-item menu-item))))
- (declare (fixnum w-h w-v))
- ;; Hack to let some applications say what items should be default.
- (dolist (cell-index *multiple-menu-cells-to-select*)
- (ask menu-item (cell-select (index-to-cell cell-index))))
- ;; Alpha keystrokes will scroll sequence item to the first entry starting
- ;; with <char>. (Assumes list is sorted.)
- (defobfun (window-key-event-handler the-dialogue) (char &aux cell)
- (if (graphic-char-p char)
- (ask menu-item
- ;; find first cell matching (in the first dimension)
- (dotimes (c (point-v (table-dimensions)) (ed-beep))
- (setf cell (make-point 0 c))
- (when (char-equal (aref (format nil "~A" (cell-contents cell)) 0)
- char)
- ;; Select it and scroll to it. (Not deselecting since multiple.)
- (cell-select cell)
- (scroll-to-cell cell)
- (return nil))))
- (usual-window-key-event-handler char)))
- (modal-dialog the-dialogue)))
-
- ;; Multiple menu choose no longer returns two values, so I have to hack
- ;; a check for whether user returned something.
- #+:TI
- (do ((chosen nil))
- ((and chosen (if (member '|none of these| chosen) (null (cdr chosen)) t))
- (if (eq (first chosen) '|none of these|) nil chosen))
- (setq chosen
- (tv:multiple-menu-choose (make-item-list (cons '|none of these| choices))
- message)))
- #+:W9000
- (do ((next-choice nil)
- (selected-items nil)
- (menu-items (nconc (split-lines (trim-right-margin message 60))
- (list :line
- " (Choose one of the listed items with the mouse) ")
- (make-item-list choices)
- (list :line '(" I am done choosing " :done)))))
- ((eq next-choice :done) selected-items)
- (setq next-choice
- (second
- (make-and-activate-menu
- *popup-fd* " Multiple Menu Dialogue "
- (if selected-items
- (append menu-items
- `(:line
- ,(format nil " CURRENTLY SELECTED: ")
- ,(format nil " ~A " selected-items)
- " (Select an item to add, or re-select it to delete) "))
- menu-items))))
- (if (member next-choice choices :test #'equal)
- (if (member next-choice selected-items :test #'equal)
- (setq selected-items
- (remove next-choice selected-items :test #'equal))
- (push next-choice selected-items))))
- #-(or :ccl :ti :W9000)
- (loop
- (if (null choices) (return nil))
- (let ((chosen nil))
- (format T "~%~A" (trim-right-margin message 60))
- (format T "~{~% ~35S~^ ~S~}" choices)
- (format T "~%(Enter list of choices):")
- (setq chosen (read))
- (cond ((null (listp chosen))
- (format T "~%*** This primitive interface requires a LIST!"))
- ((set-difference chosen choices :test #'equal)
- (format T
- "~%*** Your response contains an illegal item. Each Lisp form must be~
- ~% identical to one of the above (symbols in the same package, etc.)."))
- ((return chosen))))))
-
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- (provide :dialogue)
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- ;;; EOF
-